package com.tech.platform.cms.databases.elasticsearch.mapper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.frameworkset.orm.annotation.ESId;
import com.frameworkset.orm.annotation.ESIndex;
import com.google.common.collect.Maps;
import org.frameworkset.elasticsearch.ElasticSearchException;
import org.frameworkset.elasticsearch.boot.BBossESStarter;
import org.frameworkset.elasticsearch.client.ClientInterface;
import org.frameworkset.elasticsearch.entity.ESDatas;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;

/**
 * @author shengqiang.wu
 * @date 2020/3/18/018 12:15
 * @desc
 */
public class BaseElasticSearchMapper<T>{

    @Autowired
    private BBossESStarter bbossESStarter;

    private ClientInterface getClientInterface(){
        return bbossESStarter.getRestClient();
    }

    private Class<T> getParameterizedTypeClass(){
        return ( Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
     * 判断T 对象实例是否被ESIndex 注解
     */
    private void judgeHasUsedAnnotation(){
        Class<T> modelClz = getParameterizedTypeClass();
        Annotation esIndexAnno = modelClz.getDeclaredAnnotation(ESIndex.class);
        if(esIndexAnno==null){
            throw new ElasticSearchException("ESIndex must be use in "+modelClz.getName());
        }
    }

    /**
     * 获取 T 对象注解的indexName
     * @return
     */
    private String getIndexName(){
        Class<T> modelClz = getParameterizedTypeClass();
        ESIndex esIndexAnno = modelClz.getDeclaredAnnotation(ESIndex.class);
        if(esIndexAnno==null){
            throw new ElasticSearchException("ESIndex must be use in "+modelClz.getName());
        }
        return esIndexAnno.name();
    }

    /**
     * 获取documentId
     * @param data
     * @return
     */
    private String getDocumentId(T data) throws Exception{
        Class modelClz = data.getClass();
        Field[] fields = modelClz.getDeclaredFields();
        String dId = null;
        if(ArrayUtil.isNotEmpty(fields)){
            for (Field field : fields){
                ESId esId = field.getAnnotation(ESId.class);
                if(ObjectUtil.isNotNull(esId)){
                    field.setAccessible(true);
                    Object val = field.get(data);
                    if(ObjectUtil.isNull(val)){
                        throw new ElasticSearchException("ESId field value can not be null,clz:"+modelClz.getName());
                    }
                    dId = val.toString();
                    break;
                }
            }
        }
        if(StrUtil.isBlank(dId)){
            throw new ElasticSearchException("ESId must be use in "+modelClz.getName());
        }
        return dId;
    }

    /**
     * 保存单个文档,文档必须被ESIndex 注解
     * @param data
     * @return
     * @throws ElasticSearchException
     */
    public boolean save(T data) throws ElasticSearchException{
        judgeHasUsedAnnotation();
        String result =getClientInterface().addDocument(data);
        return true;
    }

    /**
     * 更新文档,如果不存在,不会变成插入
     * @param data
     * @return
     * @throws Exception
     */
    public boolean update(T data) throws Exception{
        judgeHasUsedAnnotation();
        String path = getIndexName()+"/_update/"+getDocumentId(data);
        Map<String,Object> params = Maps.newHashMap();
        params.put("doc",data);
        String result = getClientInterface().updateByPath(path,JSON.toJSONString(params));
        return true;
    }

    /**
     * 根据Id删除文档
     * @param documentId
     * @return
     */
    public boolean deleteById(Object documentId){
        judgeHasUsedAnnotation();
        String result = getClientInterface().deleteDocumentNew( getIndexName(),documentId.toString());
        return true;
    }

    public T getDocumentById(Object documentId){
        judgeHasUsedAnnotation();
        T result = getClientInterface().getDocument(getIndexName(),documentId.toString(),getParameterizedTypeClass());
        return result;
    }
}
